using System;
using System.Data;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Data.SqlClient;
using System.Linq;

namespace EntityFramework.ServiceLayer
{
    public interface IRepository{}
    //public interface IRepository<TEntity, TId> : IReadOnlyRepository<TEntity, TId>
    //{
    //    /// <summary>
    //    /// Persists the given entity.
    //    /// Does an insert in case the entity hasn't an id yet, and an update in case the entity already has an id.
    //    /// </summary>
    //    /// <param name="entity"></param>
    //    TId Persist(TEntity entity);

    //    /// <summary>
    //    /// Inserts the given entity.
    //    /// </summary>
    //    /// <param name="entity"></param>
    //    void Insert(TEntity entity);

    //    /// <summary>
    //    /// Updates the given entity.
    //    /// </summary>
    //    /// <param name="entity"></param>
    //    void Update(TEntity entity);

    //    /// <summary>
    //    /// Deletes the given entity.
    //    /// </summary>
    //    /// <param name="entity"></param>
    //    void Delete(TEntity entity);

    //    /// <summary>
    //    /// Deletes the entity with the given id.
    //    /// </summary>
    //    /// <param name="id"></param>
    //    void Delete(TId id);
    //}

    public class Repository<TEntity, TId> : ReadOnlyRepository<TEntity, TId>
        //,IRepository<TEntity, TId>
        ,IRepository
        where TEntity : EntityObject, IEntity<TId>
    {
        
        public TId Persist(TEntity entity)
        {
            if (entity.EntityKey == null || entity.EntityKey.IsTemporary)
            {
                Insert(entity);
            }
            else
            {
                Update(entity);
            }

            return entity.Id;
        }

        public void Insert(TEntity entity)
        {
            ObjectContext.AddObject(entity);
            ObjectContext.SaveChanges();
        }

        public void Update(TEntity entity)
        {
            //WHY: This is a dirty hack to avoid Optimistic Concurrency Problems
            ObjectContext.Refresh(RefreshMode.ClientWins, entity);
            ObjectContext.SaveChanges();
            
        }

        public void Delete(TEntity entity)
        {
            ObjectContext.DeleteObject(entity);
            ObjectContext.SaveChanges();
        }

        public void Delete(IQueryable<TEntity> queryable)
        {
            foreach (var entity in queryable.ToList())
            {
                Delete(entity);
            }
        }

        public void Delete(TId id)
        {
            var entity = Get(id);
            ObjectContext.DeleteObject(entity);
            ObjectContext.SaveChanges();
        }

        public void ExecuteNonQuery(string query, params QueryParameter[] queryParameters)
        {
            var connection = ObjectContext.SqlConnection();
            if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }
            var command = connection.CreateCommand();
            command.CommandText = query;
            command.Transaction = ObjectContext.SqlTransaction();

            foreach (var parameter in queryParameters)
            {
                command.Parameters.Add(new SqlParameter
                {
                    ParameterName = parameter.ParameterName,
                    Value = (parameter.Value ?? DBNull.Value)
                });
            }

            command.ExecuteNonQuery();
        }

        protected string GetSortExpression(string sortField, string defaultSortField, ResultSortDirection sortDirection)
        {
            return (string.IsNullOrEmpty(sortField) ? defaultSortField : sortField) + " " + ((sortDirection == ResultSortDirection.Ascending) ? "ASC" : "DESC");
        }
    }
}